// var evalJS = eval;

function SPair(left, right) {
    this.left = left;
    this.right = right;
}
function SSymbol(value) {
    this.value = value;
}
SSymbol.prototype.toString = function () {
    return this.value.toLowerCase()
}
// null?
function null_QuestionMark_(x) {
    return x === undefined || x === null || (Array.isArray(x) && x.length === 0)
}
// pair?
function pair_QuestionMark_(x) {
    return x instanceof SPair || (Array.isArray(x) && x.length > 0)
}
// list?
function list_QuestionMark_(x) {
    return Array.isArray(x)
}
// number?
function number_QuestionMark_(x) {
    return typeof x === "number"
}
// string?
function string_QuestionMark_(x) {
    return typeof x === "string"
}
// boolean?
function boolean_QuestionMark_(x) {
    return typeof x === "boolean"
}
// symbol?
function symbol_QuestionMark_(x) {
    return x instanceof SSymbol
}
// procedure?
function procedure_QuestionMark_(x) {
    return !pair_QuestionMark_(x) && typeof x === "function"
}
// eq?
function eq_QuestionMark_(a, b) {
    if (symbol_QuestionMark_(a) && symbol_QuestionMark_(b)) {
        return a.value.toLowerCase() === b.value.toLowerCase()
    }
    if (null_QuestionMark_(a) && null_QuestionMark_(b)) {
        return true
    }
    return a === b
}
// eqv?
function eqv_QuestionMark_(a, b) {
    if (symbol_QuestionMark_(a) && symbol_QuestionMark_(b)) {
        return a.value.toLowerCase() === b.value.toLowerCase()
    }
    if (null_QuestionMark_(a) && null_QuestionMark_(b)) {
        return true
    }
    return a === b
}
// equal?
function equal_QuestionMark_(a, b) {
    if (a instanceof SPair && b instanceof SPair) {
        return equal_QuestionMark_(a.left, b.left) && equal_QuestionMark_(a.right, b.right)
    }
    if (Array.isArray(a) && Array.isArray(b)) {
        if (a.length !== b.length) {
            return false
        }
        const n = a.length
        for (let i = 0; i < n; i++) {
            if (!equal_QuestionMark_(a[i], b[i])) {
                return false
            }
        }
        return true
    }
    return eqv_QuestionMark_(a, b)
}
function cons(a, b) {
    if (null_QuestionMark_(b)) {
        return [a]
    }
    if (Array.isArray(b)) {
        return [a].concat(b)
    }
    return new SPair(a, b)
}
function car(x) {
    if (null_QuestionMark_(x)) {
        console.error("car on nil")
    } else if (Array.isArray(x)) {
        if (x.length === 0) {
            console.error("car on empty list")
        }
        return x[0]
    } else if (x instanceof SPair) {
        return x.left
    } else {
        console.error("car on atom")
    }
}
function cdr(x) {
    if (null_QuestionMark_(x)) {
        return null
    } else if (Array.isArray(x)) {
        if (x.length === 0) {
            console.error("car on empty list")
        }
        return x.slice(1)
    } else if (x instanceof SPair) {
        return x.right
    } else {
        console.error("cdr on atom")
    }
}
function list(...x) {
    return [...x]
}
function map(f, xs) {
    if (!Array.isArray(xs)) {
        console.error("map on not list")
    } else {
        return xs.map(f)
    }
}
function length(xs) {
    if (!Array.isArray(xs)) {
        console.error("length on not list")
        return 0
    } else {
        return xs.length
    }
}
function append(xs, a) {
    if (!Array.isArray(xs)) {
        console.error("append on non list")
        return [a]
    }
    return xs.concat(a)
}
function filter(p, xs) {
    if (!Array.isArray(xs)) {
        console.error("filter on non list")
        return xs
    }
    let a = []
    for (let x of xs) {
        if (p(x)) {
            a.push(x)
        }
    }
    return a
}

// set-car!
function set__car_Important_(xs, x) {
    if (xs instanceof SPair) {
        xs.left = x
    } else {
        if (xs.length > 0) {
            xs[0] = x
        } else {
            console.error("try to set-car! on empty list")
        }
    }
}
// set-cdr!
function set__cdr_Important_(xs, x) {
    if (xs instanceof SPair) {
        xs.right = x
    } else {
        if (xs.length > 0) {
            xs.splice(1)
            xs.push(...x)
        } else {
            console.error("try to set-cdr! on empty list")
        }
    }
}
// set-of!
function set__of_Important_(obj, key, value) {
    obj[key] = value
}
function display(...x) {
    console.error(...x)
}
function error(...x) {
    console.error(...x)
    throw new SSymbol("error")
}
// of
function _of_(obj, method) {
    return obj[method]
}
function apply(method, args) {
    return method.apply(null, args)
}
function and(...xs) {
    let s = xs[0] && xs[1]
    for (let i = 2; i < xs.length; i++) {
        s = s && xs[i]
    }
    return s
}
function or(...xs) {
    let s = xs[0] || xs[1]
    for (let i = 2; i < xs.length; i++) {
        s = s || xs[i]
    }
    return s
}
function not(a) {
    return !a
}
function memq(a, xs) {
    if (null_QuestionMark_(xs)) {
        return false
    }
    for (let i = 0; i < xs.length; i++) {
        if (eqv_QuestionMark_(a, xs[i])) {
            return xs.slice(i)
        }
    }
    return false
}

// (begin 
//   (eval '((define a 3) a))
//   a)
// will error
// (begin
//   (load "sqrt")
//   (d)
evalSS = function (code) {
    const { Error } = require('./error');
    const { token, Comment, } = require('./token');
    const { parse, Apply } = require('./parse')
    const { compile } = require('./compile')
    const ast = parse(token(code)) // it's a begin
    if (ast instanceof Error) {
        // error of parse lisp
        // ex: ( (define a 3) (display b) )
        throw ast
    }
    const buildinSSJS = compile(ast)
    console.debug(buildinSSJS)
    return evalJS(buildinSSJS) // may raise js error
}

// function load(file) { // load will be overwrite when compile
//     return runtimeLoad(file)
// }

// === generated by cxr-gen.js ===

function caar(x) { return car(car(x)) }
function cadr(x) { return car(cdr(x)) }
function cdar(x) { return cdr(car(x)) }
function cddr(x) { return cdr(cdr(x)) }
function caaar(x) { return car(car(car(x))) }
function caadr(x) { return car(car(cdr(x))) }
function cadar(x) { return car(cdr(car(x))) }
function caddr(x) { return car(cdr(cdr(x))) }
function cdaar(x) { return cdr(car(car(x))) }
function cdadr(x) { return cdr(car(cdr(x))) }
function cddar(x) { return cdr(cdr(car(x))) }
function cdddr(x) { return cdr(cdr(cdr(x))) }
function caaaar(x) { return car(car(car(car(x)))) }
function caaadr(x) { return car(car(car(cdr(x)))) }
function caadar(x) { return car(car(cdr(car(x)))) }
function caaddr(x) { return car(car(cdr(cdr(x)))) }
function cadaar(x) { return car(cdr(car(car(x)))) }
function cadadr(x) { return car(cdr(car(cdr(x)))) }
function caddar(x) { return car(cdr(cdr(car(x)))) }
function cadddr(x) { return car(cdr(cdr(cdr(x)))) }
function cdaaar(x) { return cdr(car(car(car(x)))) }
function cdaadr(x) { return cdr(car(car(cdr(x)))) }
function cdadar(x) { return cdr(car(cdr(car(x)))) }
function cdaddr(x) { return cdr(car(cdr(cdr(x)))) }
function cddaar(x) { return cdr(cdr(car(car(x)))) }
function cddadr(x) { return cdr(cdr(car(cdr(x)))) }
function cdddar(x) { return cdr(cdr(cdr(car(x)))) }
function cddddr(x) { return cdr(cdr(cdr(cdr(x)))) }
